﻿using System;
using System.Text;

namespace LK.Math
{
    /// <summary>
    /// 表示以位为单位进行存储的数组
    /// </summary>
    public partial class Bits
    {
        //绑定数组
        ulong[] array;
        /// <summary>
        /// 获取内部绑定的数组
        /// </summary>
        public ulong[] Data { get { return array; } }

        /// <summary>
        /// 获取或者设置指定位置的位内容
        /// </summary>
        /// <param name="idx">位索引，从0开始</param>
        /// <returns>指定位为1返回true，否则返回false</returns>
        /// <remarks>get方法不会抛出异常，set方法在索引超出内部数组存储范围时将抛出异常</remarks>
        public bool this[int idx] {
            get {
                if (idx < 0 || array == null || idx>=((long)array.Length<<6))
                    return false;
                return (array[idx >> 6] & (1ul << idx)) != 0;
            }
            set {
                if (idx < 0 || array == null || idx>=((long)array.Length<<6))
                    throw new IndexOutOfRangeException();
                if(value)array[idx >> 6] |= (1ul << idx);
                else array[idx >> 6] &= ~(1ul << idx);
            }
        }

        /// <summary>
        /// 绑定数组所含位的数量
        /// </summary>
        public int Length { 
            get { return array == null ? 0 : (array.Length <<6); } 
        }

        /// <summary>
        /// 翻转指定位的内容
        /// </summary>
        /// <param name="idx">位索引</param>
        public void Flip(int idx) {
            array[idx >> 6] ^= (1ul << idx);
        }

        /// <summary>
        /// 构造一个元素数量为0的数组
        /// </summary>
        public Bits() { }

        /// <summary>
        /// 构造一个数据副本。将会对内部数据进行拷贝
        /// </summary>
        /// <returns>复制的Bits对象</returns>
        public Bits Clone() {
            var v = new Bits();
            v.array = array.Clone() as ulong[];
            return v;
        }

        /// <summary>
        /// 引用指定的数组来创建
        /// </summary>
        /// <param name="arr">引用的数组</param>
        /// <returns>引用该数组的Bits对象</returns>
        static public Bits CreateFromArray(ulong[] arr) {
            Bits b = new Bits();
            b.array = arr;
            return b;
        }
        //获取该数组的一部分构成新的数组，新数组多出的部分用0填充
        public Bits SubBits(int startIndex)
        {
            return SubBits(startIndex, Length - startIndex);
        }
        /// <summary>
        /// 获取该数组的一部分构成的新数组，新数组多出的部分用0填充
        /// </summary>
        /// <param name="startIndex">起始位置</param>
        /// <param name="length">连续位的数量</param>
        /// <returns>新的Bits对象，不共用本对象内容</returns>
        Bits SubBits(int startIndex, int length) {
            ulong[] dat = new ulong[(length + 63) >> 6];
            Copy(array, startIndex, length, dat, 0);
            return CreateFromArray(dat);
        }
        /// <summary>
        /// 往指定的位索引写入64比特数据。超出内部数组范围的数据将会被丢弃
        /// </summary>
        /// <param name="value">写入的64位数据</param>
        /// <param name="startIndex">位索引</param>
        public void Set64(ulong value, int startIndex = 0) {
            int lidx = startIndex >> 6;
            int mov = startIndex & 63;
            if (array == null) return;
            if (mov == 0)
            {
                if (lidx >= 0 && lidx < array.Length)
                    array[lidx] = value;
                return;
            }
            if (lidx >= 0 && lidx < array.Length)
                array[lidx] = (array[lidx] & ((~0ul) >> (64 - mov))) | (value << mov);
            if (lidx >-1 && lidx < array.Length-1)
                array[lidx+1] = (array[lidx+1] & ((~0ul) << mov)) | (value >> (64 - mov));
        }
        /// <summary>
        /// 从指定的位索引读取64比特数据。超出内部数组范围的部分用0填充
        /// </summary>
        /// <param name="startBitPosition">位索引</param>
        /// <returns>获取到的64位数据</returns>
        public ulong Get64(int startBitPosition) {
            if (array == null) return 0;
            int idx = startBitPosition >> 6;
            int mov = startBitPosition & 63;
            ulong low = idx<0?0:idx>=array.Length?0:array[idx]>>mov;
            if (mov == 0) return low;
            ulong high = idx < -1 ? 0 : idx >= array.Length-1 ? 0 : array[idx+1] <<(64-mov); ;
            return low | high;
        }
        /// <summary>
        /// 转为内部数据二进制的字符串描述
        /// </summary>
        /// <returns>描述字符串</returns>
        public override string ToString()
        {
            return ToString(0);
        }
        public string ToString(int idx) { return ToString(0, array == null ? 0 : array.Length * 64); }
        /// <summary>
        /// 输出内部数据的二进制描述
        /// </summary>
        /// <param name="idx">输出的起始二进制位索引</param>
        /// <param name="len">输出的二进制数量。小于0时逆序输出</param>
        /// <returns>返回二进制表示</returns>
        public string ToString(int idx,int len) {
            StringBuilder sb = new StringBuilder();
            if (len >= 0)
                for (int i = idx+len; i >idx;)
                    sb.Append(this[--i] ? '1' : '0');
            else
            {
                for (int i = idx; i < idx - len; i++)
                    sb.Append(this[i] ? '1' : '0');
            }
            return sb.ToString();
        }

        public override bool Equals(object obj)
        {
            if (obj.GetType() == typeof(Bits)) {
                return Equals(obj as Bits);
            }
            return base.Equals(obj);
        }
        public bool Equals(Bits obj) {
            if (null == array || null == obj.array) return array == obj.array;
            int len = array.Length > obj.array.Length ? obj.array.Length : array.Length;
            for (int i = 0; i < len; ++i)
                if (array[i] != obj.array[i])
                    return false;
            while (len < array.Length)
                if (array[len++] != 0)
                    return false;
            while (len < obj.array.Length)
                if (obj.array[len++] != 0)
                    return false;
            return true;
        }
        public override int GetHashCode() {
            return base.GetHashCode();
        }
        /// <summary>
        /// 重新按指定比特数量所需最小数组长度调整数组元素数量，往小调整时，按低位对齐保留原有数据
        /// </summary>
        /// <param name="bitsCount">存放的比特数量</param>
        public void Resize(int bitsCount) {
            int len = (bitsCount + 63) >> 6;
            if (array == null)
            {
                if (len > 0)
                    array = new ulong[len];
            }
            else 
                Array.Resize(ref array, len);
        }
    }
}
